package ldh.im.fxbase.service;

import ldh.im.dto.base.OkResp;
import ldh.im.dto.business.group.*;
import ldh.im.dto.business.item.GroupInfoItem;
import ldh.im.dto.enums.GroupType;
import ldh.im.fxbase.dao.ImGroupDao;
import ldh.im.fxbase.dao.ImGroupUserDao;
import ldh.im.fxbase.dao.ImSessionDao;
import ldh.im.fxbase.data.event.EventBusFactory;
import ldh.im.fxbase.data.event.SessionChange;
import ldh.im.fxbase.data.pojo.ImGroup;
import ldh.im.fxbase.data.pojo.ImGroupUser;
import ldh.im.fxbase.data.pojo.ImSession;
import ldh.im.fxbase.data.pojo.ImUser;
import ldh.im.fxbase.data.pojo.status.ImGroupStatus;
import ldh.im.fxbase.data.pojo.status.ImGroupType;
import ldh.im.fxbase.data.pojo.status.ImGroupUserStatus;
import ldh.im.fxbase.data.pojo.status.ImSessionStatus;
import ldh.im.fxbase.rpc.service.RpcGroupService;
import ldh.im.fxbase.util.BeanFactory;
import ldh.im.fxbase.util.ImCacheFactory;
import ldh.im.fxbase.util.RpcUtil;
import ldh.rpc.util.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.SQLException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class ImGroupService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ImGroupService.class);

    private ImGroupDao imGroupDao = BeanFactory.getInstance().getBean(ImGroupDao.class);
    private ImGroupUserDao imGroupUserDao = BeanFactory.getInstance().getBean(ImGroupUserDao.class);

    private ImSessionDao imSessionDao = BeanFactory.getInstance().getBean(ImSessionDao.class);

    public ImSession createGroup(String userId, GroupCreateReq groupCreateReq) throws SQLException {
        Set<ImUser> users = groupCreateReq.getUserIdSet().stream().map(u->ImCacheFactory.getInstance().getUser(u)).collect(Collectors.toSet());
        ImSession imSession = buildImGroup(userId, users);
        imSession.setGroupCode(groupCreateReq.getGroupId());
        imSession.setServerId(groupCreateReq.getSessionId());
        imSession.setStatus(ImSessionStatus.enable);

        ImGroup imGroup = imSession.getImGroup();
        imGroup.setServerId(groupCreateReq.getGroupId());
        imGroup.setCode(groupCreateReq.getGroupId());
        imGroup.setName(groupCreateReq.getGroupName());
        imGroup.setDescription(groupCreateReq.getGroupDesc());

        createImGroup(imSession);

        ImCacheFactory.getInstance().putImGroup(imGroup.getId(), imGroup);
        EventBusFactory.getInstance().post(new SessionChange(imSession.getId()));

        return imSession;
    }

    public ImSession addToGroup(String userId, GroupAddReq groupAddReq) throws SQLException {
        boolean groupChange = false;
        boolean sessionChange = false;

        ImGroup imGroup = imGroupDao.getByUserIdAndId(userId, groupAddReq.getGroupId());
        if (imGroup == null) { // 邀请用户进群
            return createGroup(userId, groupAddReq);
        }
        ImSession imSession = imSessionDao.getByGroupCodeAndUserId(groupAddReq.getGroupId(), userId);

        if (groupAddReq.getGroupDesc() != null && !groupAddReq.getGroupDesc().equals(imGroup.getDescription())) {
            imGroup.setDescription(groupAddReq.getGroupDesc());
            groupChange = true;
        }

        if (groupAddReq.getGroupName() != null && !imGroup.getName().equals(groupAddReq.getGroupName())) {
            imGroup.setName(groupAddReq.getGroupName());

            imSession.setImGroup(imGroup);
            imSession.setName(groupAddReq.getGroupName());
            sessionChange = true;
        }

        if (groupChange) {
            imGroup.setUpdateTime(new Date());
            imGroupDao.update(imGroup);
        }

        if (sessionChange) {
            imSessionDao.updateSession(imSession);
        }
        Set<ImGroupUser> groupUsers = toGroupUsers(imGroup.getId(), groupAddReq.getUserIdSet());

        Set<ImGroupUser> dbGroupUserSet = imGroup.getGroupUserSet();
        Set<ImGroupUser> insertImGroupUserSet = groupUsers.stream().filter(gu->!dbGroupUserSet.contains(gu)).collect(Collectors.toSet());
        imGroup.setGroupUserSet(insertImGroupUserSet);
        imGroupUserDao.batchInserts(imGroup);

        imGroup = imGroupDao.getByUserIdAndId(userId, imGroup.getId());
        imSession.setImGroup(imGroup);
        ImCacheFactory.getInstance().putImGroup(imGroup.getId(), imGroup);
        return imSession;
    }

    public ImSession exitGroup(String userId, GroupExitReq groupExitReq) throws SQLException {
        ImGroup imGroup = imGroupDao.getByUserIdAndId(userId, groupExitReq.getGroupId());
        List<ImGroupUser> updateGroupUsers = new ArrayList<>();
        for (ImGroupUser groupUser : imGroup.getGroupUserSet()) {
            for (String uid : groupExitReq.getUserIdSet()) {
                if (groupUser.getUserId().equals(uid)) {
                    groupUser.setStatus(ImGroupUserStatus.disable);
                    groupUser.setUpdateTime(new Date());
                    updateGroupUsers.add(groupUser);
                }
            }
        }
        if (updateGroupUsers.size() == imGroup.getGroupUserSet().size()) {
            imGroup.setStatus(ImGroupStatus.dissolution);
            imGroup.setUpdateTime(new Date());
            imGroupDao.update(imGroup);
        }

        imGroupUserDao.batchUpdates(updateGroupUsers);

        ImSession imSession = imSessionDao.getByGroupCodeAndUserId(imGroup.getCode(), userId);
        imSession.setImGroup(imGroup);
        ImCacheFactory.getInstance().putImGroup(imGroup.getId(), imGroup);
        return imSession;
    }

    public ImSession delGroup(String userId, GroupDelReq groupDelReq) throws SQLException {
        ImGroup imGroup = imGroupDao.getByUserIdAndId(userId, groupDelReq.getGroupId());
        imGroup.setStatus(ImGroupStatus.dissolution);
        imGroup.setUpdateTime(new Date());
        imGroupDao.update(imGroup);
        ImCacheFactory.getInstance().putImGroup(imGroup.getId(), imGroup);

        ImSession session = imSessionDao.getByGroupCodeAndUserId(imGroup.getCode(), userId);
        session.setStatus(ImSessionStatus.disable);
        session.setUpdateTime(new Date());
        imSessionDao.updateSession(session);
        return session;
    }

    /**
     * 同步拉取的群信息
     * @param userId
     * @param groupInfoItems
     * @throws SQLException
     */
    public void saveOrUpdates(String userId, List<GroupInfoItem> groupInfoItems, boolean needCreateSession) throws SQLException {
        if (groupInfoItems.size() < 1) return;

        List<ImGroup> imGroupList = groupInfoItems.stream().map(ginfo->toGroup(userId, ginfo, GroupType.chat)).collect(Collectors.toList());
        Set<ImGroup> insertImGroupSet = new HashSet<>();
        Set<ImGroup> updateImGroupSet = new HashSet<>();
        Set<ImGroupUser> updateImGroupUserSet = new HashSet<>();
        Set<ImSession> updateSessionSet = new HashSet();

        List<ImGroup> dbGroups = imGroupDao.getImGroupByCodes(userId, imGroupList.stream().map(g->g.getCode()).collect(Collectors.toSet()));
        Map<String, ImGroup> groupMap = dbGroups.stream().collect(Collectors.toMap(ImGroup::getCode, Function.identity()));
        for(ImGroup imGroup : imGroupList) {
            ImGroup dbGroup = groupMap.get(imGroup.getCode());
            if (dbGroup == null) { // 新增
                insertImGroupSet.add(imGroup);
            } else if (imGroup.getName() != null){ // 群信息变更了
                imGroup.setId(dbGroup.getId());
                if(imGroup.getGroupUserSet() != null) {
                    imGroup.getGroupUserSet().forEach(gu->gu.setGroupId(dbGroup.getId()));
                }
                updateImGroupSet.add(imGroup);

                ImSession session = imSessionDao.getByGroupCodeAndUserId(imGroup.getCode(), userId);
                session.setName(imGroup.getName());
                session.setUpdateTime(new Date());
                updateSessionSet.add(session);
            } else { // 仅组员信息发生变化
                imGroup.setId(dbGroup.getId());
                if(imGroup.getGroupUserSet() != null) {
                    imGroup.getGroupUserSet().forEach(gu->gu.setGroupId(dbGroup.getId()));
                    updateImGroupUserSet.addAll(imGroup.getGroupUserSet());
                }
            }
        }

        List<ImGroup> groups = insertImGroupSet.stream().collect(Collectors.toList());
        imGroupDao.batchInserts(groups);

        imGroupDao.batchUpdates(updateImGroupSet);
        List<ImGroupUser> groupUsers = updateImGroupSet.stream().flatMap(g->g.getGroupUserSet().stream()).collect(Collectors.toList());
        groupUsers.addAll(updateImGroupUserSet);
        imGroupUserDao.insertOrUpdates(groupUsers);

        imSessionDao.batchUpdates(updateSessionSet.stream().collect(Collectors.toList()));

        if (needCreateSession) {
            this.createSession(userId, groups);
        }
    }

    public ImGroup pullGroup(String userId, String groupCode) throws SQLException {
        RpcGroupService rpcGroupService = RpcUtil.getRpcBootstrap().getRpcConfig().getClientBean(RpcGroupService.class);
        GroupInfoReq groupInfoReq = new GroupInfoReq();
        groupInfoReq.setGroupId(groupCode);
        GroupInfoItem groupInfoItem = rpcGroupService.pullGroupInfo(groupInfoReq);
        this.saveOrUpdates(userId, Arrays.asList(groupInfoItem), true);
        return imGroupDao.getByUserIdAndId(userId, groupCode);
    }

    public void sendSuccess(ImGroup imGroup, String sessionServerId) throws SQLException {
        imGroup.setServerId(imGroup.getCode());
        imGroup.setUpdateTime(new Date());
        imGroup.setStatus(ImGroupStatus.enable);
        imGroupDao.update(imGroup);

        ImSession imSession = imSessionDao.getByGroupCodeAndUserId(imGroup.getCode(), imGroup.getCurrentUserId());
        if (imSession.getServerId() == null) {
            imSession.setServerId(sessionServerId);
            imSession.setUpdateTime(new Date());
            imSessionDao.updateSession(imSession);
        }
    }

    /**
     * 构建群，并同时构建群会话
     * @param userId
     * @param imUsers
     * @return
     */
    public ImSession buildImGroup(String userId, Set<ImUser> imUsers) {
        ImGroup group = new ImGroup();
        group.setGroupUserSet(imUsers.stream().map(u->buildGroupUser(u)).collect(Collectors.toSet()));
        String groupName = buildGroupName(imUsers);
        group.setName(groupName);
        group.setGroupType(ImGroupType.chat);
        group.setCode(UUID.randomUUID().toString());
        group.setDescription(null);
        group.setStatus(ImGroupStatus.none);
        group.setCurrentUserId(userId);
        group.setOwnerId(userId);

        ImSession imSession = buildSession(userId, group);
        return imSession;
    }

    /**
     * 创建群， 并创建群会话
     * @param imSession
     */
    public void createImGroup(ImSession imSession) throws SQLException {
        ImGroup group = imSession.getImGroup();
        imGroupDao.insertImGroup(group);
        imSessionDao.insertSession(imSession);
    }

    public void rpcCreateGroup(ImSession imSession) throws SQLException {
        RpcGroupService rpcGroupService = RpcUtil.getRpcBootstrap().getRpcConfig().getClientBean(RpcGroupService.class);

        GroupCreateReq groupCreateReq = new GroupCreateReq();
        groupCreateReq.setGroupName(imSession.getImGroup().getName());
        groupCreateReq.setGroupId(imSession.getImGroup().getCode());
        groupCreateReq.setGroupDesc(imSession.getImGroup().getDescription());
        groupCreateReq.setUserIdSet(imSession.getImGroup().getGroupUserSet().stream().map(u->u.getUserId()).collect(Collectors.toSet()));
        OkResp okResp = rpcGroupService.groupCreate(groupCreateReq);
        LOGGER.info("groupCreate result: {}", JsonUtil.toJson(okResp));

        String sessionId = okResp.getOtherInfo();
        ImGroup imGroup = imSession.getImGroup();
        imGroup.setServerId(imGroup.getCode());
        imGroup.setUpdateTime(new Date());
        if (imGroup.getVersion() == null) imGroup.setVersion(0L);
        if (imGroup.getStatus() == ImGroupStatus.none) imGroup.setStatus(ImGroupStatus.enable);
        imGroupDao.updateServerId(imGroup);
        imSession.setGroupCode(imSession.getImGroup().getServerId());
        imSession.setStatus(ImSessionStatus.enable);
        imSession.setUpdateTime(new Date());
        imSession.setServerId(sessionId);
        imSessionDao.updateSessionGroupCode(imSession);

        ImCacheFactory.getInstance().putImGroup(imGroup.getId(), imGroup);
    }

    public ImGroupUser buildGroupUser(ImUser u) {
        ImGroupUser groupUser = new ImGroupUser();
        groupUser.setUserId(u.getUserId());
        groupUser.setRemark(null);
        groupUser.setShowName(true);
        groupUser.setWaking(true);
        groupUser.setExt1(null);
        groupUser.setExt2(null);
        groupUser.setStatus(ImGroupUserStatus.enable);
        groupUser.setUser(ImCacheFactory.getInstance().getUser(u.getUserId()));
        return groupUser;
    }

    private Set<ImGroupUser> toGroupUsers(long groupId, Set<String> userIdSet) {
        Set<ImGroupUser> groupUsers = new LinkedHashSet<>();
        if (userIdSet != null) {
            userIdSet.forEach(u->{
                ImGroupUser groupUser = new ImGroupUser();
                ImUser imUser = ImCacheFactory.getInstance().getUser(u);
                groupUser.setUser(imUser);
                groupUser.setGroupId(groupId);
                groupUser.setUserName(imUser.getUserName());
                groupUser.setUserId(imUser.getUserId());
                groupUser.setWaking(true);
                groupUser.setShowName(true);
                groupUser.setStatus(ImGroupUserStatus.enable);
                groupUsers.add(groupUser);
            });
        }
        return groupUsers;
    }

    private ImGroup toGroup(String userId, GroupInfoItem groupInfoItem, GroupType groupType) {
        ImGroup imGroup = new ImGroup();
        imGroup.setCurrentUserId(userId);
        imGroup.setSessionId(groupInfoItem.getSessionId());
        imGroup.setServerId(groupInfoItem.getGroupId());
        imGroup.setCode(groupInfoItem.getGroupId());
        if (groupInfoItem.getGroupName() != null) {
            imGroup.setName(groupInfoItem.getGroupName());
            imGroup.setOwnerId(groupInfoItem.getOwnerId());
            imGroup.setDescription(groupInfoItem.getGroupDesc());
            imGroup.setStatus(ImGroupStatus.wrap(groupInfoItem.getStatus()));
            imGroup.setGroupType(ImGroupType.wrap(groupType));
            imGroup.setFirstLetter(groupInfoItem.getFirstLetter());
            imGroup.setVersion(0L);
        }

        Set<ImGroupUser> groupUsers = new LinkedHashSet<>();
        if (groupInfoItem.getGroupUserSet() != null) {
            groupInfoItem.getGroupUserSet().forEach(u->{
                ImGroupUser groupUser = new ImGroupUser();
                ImUser imUser = ImCacheFactory.getInstance().getUser(u.getUserId());
                groupUser.setUser(imUser);
                groupUser.setUserName(u.getUserName());
                groupUser.setUserId(imUser.getUserId());
                groupUser.setRemark(u.getRemark());
                groupUser.setShowName(u.getShowName());
                groupUser.setWaking(u.getWaking());
                groupUser.setTop(u.getTop());
                groupUser.setDisturb(u.getDisturb());
                groupUser.setExt1(u.getExt1());
                groupUser.setExt2(u.getExt2());
                groupUser.setStatus(ImGroupUserStatus.enable);
                groupUsers.add(groupUser);
            });
        }
        imGroup.setGroupUserSet(groupUsers);
        return imGroup;
    }

    private ImSession buildSession(String userId, ImGroup group) {
        ImSession imSession = ImSession.build(userId, group);

        imSession.setImGroup(group);
        imSession.setGroupCode(group.getCode());
        imSession.setName(group.getName());
        return imSession;
    }

    // 创建群会话
    private void createSession(String userId, List<ImGroup> imGroups) throws SQLException {
        List<ImSession> insertSessions = new ArrayList<>(imGroups.size());
        List<ImSession> updateSessions = new ArrayList<>(imGroups.size());

        for (ImGroup group : imGroups) {
            ImSession session =  imSessionDao.getByGroupCodeAndUserId(group.getCode(), userId);
            if (session == null) {
                session = ImSession.build(userId, group);
                insertSessions.add(session);
            } else {
                session.setName(group.getName());
                session.setUpdateTime(new Date());
                if (session.getStatus() == ImSessionStatus.created) {
                    session.setStatus(ImSessionStatus.enable);
                }
                updateSessions.add(session);
            }
        }
        imSessionDao.batchInserts(insertSessions);
        imSessionDao.batchUpdates(updateSessions);
    }

    private String buildGroupName(Set<ImUser> imUsers) {
        if (imUsers.size() < 4) {
            return imUsers.stream().map(u->u.getUserName()).collect(Collectors.joining(","));
        }
        return imUsers.stream().limit(4).map(u->u.getUserName()).collect(Collectors.joining(","));
    }

}
